import { defineComponent, markRaw, onUnmounted, ref, watch } from 'vue'
import ItemNode from '../../ItemNode/ItemNode'
import classes from '../../ItemNode/ItemNode.module.css'
import { ElInput, ElInputNumber, ElRadioButton, ElRadioGroup, ElText } from 'element-plus'
import Map from './Map.vue'
import MapRaw from './Map.vue?raw'
import { debounce, gps2Amap, baidu2Amap } from '../../utils'
import utilsRaw from '../../utils?raw'
import request from '../../request/request'
import requestRaw from '../../request/request?raw'
import { state } from '../../store/store.js'
import CodeEditor from '../../panel/CodeEditor.vue'
import Tip from '../../panel/Tip.vue'

export default class NMap extends ItemNode {
  isNMap = true
  isChartNode = true

  constructor(node?: NMap) {
    super(node)

    this.watchStateOptions.push({
      value: 'initMarkersData',
      label: '刷新点标记数据'
    })

    if (!node) {
      this.path = './components/NMap/NMap.tsx'
      this.name = '地图'

      const style = this.props.style
      this.props.style = {
        ...style,
        width: '300px',
        height: '200px',
        flexShrink: '0'
      }

      this.props.markersDataUrl = ''
      this.props.markersDataFn =
        'function (res) {\n  return [{\n    position: [\n      Number,\n      Number\n    ],\n    extData: {\n      content: String \n    }\n  }]\n}'
      this.props.option = {
        center: [113.658097, 34.745795],
        zoom: 11,
        mapStyle: 'amap://styles/normal'
      }
      this.props.coordType = 'amap'
    }

    const pComponent = this.component
    this.component = markRaw(
      defineComponent(
        (props) => {
          const markersData = ref([])

          const initMarkersData = async () => {
            if (!this.props.markersDataUrl) return

            await request({
              url: this.props.markersDataUrl.replace(/\:(\w+)/g, (match, p1) => state[p1]),
              method: 'GET'
            }).then((res) => {
              if (this.props.markersDataFn) {
                try {
                  if (!state) return

                  const resData = eval(`(${this.props.markersDataFn})`)(res)

                  resData.forEach((item) => {
                    item.position = item.position.map((i) => Number(i))
                    if (this.props.coordType == 'gps') item.position = gps2Amap(item.position)
                    if (this.props.coordType == 'baidu') item.position = baidu2Amap(item.position)
                  })
                  markersData.value = resData
                } catch (e) {}
              }
            })
          }

          const initData = async () => {
            initMarkersData()
          }

          const initDataDebounce = debounce(() => {
            markersData.value = []
            initData()
          }, 1000)

          watch(
            [
              () => this.props.markersDataUrl,
              () => this.props.markersDataFn,
              () => this.props.coordType
            ],
            initDataDebounce
          )

          initDataDebounce()
          // 解除监听state属性的函数集合
          const watchStateSet = new Set()
          // 监听依赖的state属性，响应对应方法
          watch(
            () => this.props.watchState,
            () => {
              watchStateSet.forEach((v: () => void) => v())

              this.props.watchState.forEach((watchObj: watchStateObj) => {
                if (watchObj.type == 'initMarkersData') {
                  watchStateSet.add(watch(() => state[watchObj.stateProperty], initMarkersData))
                }
              })
            },
            { immediate: true, deep: true }
          )

          onUnmounted(() => {
            watchStateSet.forEach((v: () => void) => v())
          })

          return () => (
            <pComponent>
              <Map option={this.props.option} markersData={markersData.value}></Map>
            </pComponent>
          )
        },
        {
          name: 'NMap'
        }
      )
    )

    const pView = this.view
    this.view = markRaw(
      defineComponent((props) => {
        return () => (
          <pView>
            <this.component style={{ transform: 'scale(0.32, 0.3)' }}></this.component>
          </pView>
        )
      })
    )

    const pParams = this.params
    this.params = markRaw(
      defineComponent(() => {
        return () => (
          <pParams>
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                地图样式
              </ElText>
              <div class={classes['params-item-content']}>
                <ElRadioGroup
                  modelValue={this.props.option.mapStyle}
                  onUpdate:modelValue={(v) => (this.props.option.mapStyle = v)}
                >
                  <ElRadioButton
                    value="amap://styles/normal"
                    label="标准"
                    size="small"
                  ></ElRadioButton>
                  <ElRadioButton
                    value="amap://styles/darkblue"
                    label="黑蓝"
                    size="small"
                  ></ElRadioButton>
                </ElRadioGroup>
              </div>
            </div>
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                地图缩放级别
              </ElText>
              <div class={classes['params-item-content']}>
                <ElInputNumber
                  modelValue={Number(this.props.option.zoom) || 0}
                  onUpdate:modelValue={(v) => (this.props.option.zoom = v)}
                  min={0}
                  controls-position="right"
                  size="small"
                ></ElInputNumber>
              </div>
            </div>
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                地图中心
              </ElText>
              <div class={classes['params-item-content']}>
                <ElInputNumber
                  modelValue={Number(this.props.option.center[0]) || 0}
                  onUpdate:modelValue={(v) => (this.props.option.center[0] = v)}
                  min={0}
                  controls={false}
                  size="small"
                ></ElInputNumber>
                <ElInputNumber
                  modelValue={Number(this.props.option.center[1]) || 0}
                  onUpdate:modelValue={(v) => (this.props.option.center[1] = v)}
                  min={0}
                  controls={false}
                  size="small"
                ></ElInputNumber>
              </div>
            </div>
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                原坐标系
              </ElText>
              <div class={classes['params-item-content']}>
                <ElRadioGroup
                  modelValue={this.props.coordType}
                  onUpdate:modelValue={(v) => (this.props.coordType = v)}
                >
                  <ElRadioButton value="amap" label="高德" size="small"></ElRadioButton>
                  <ElRadioButton value="gps" label="gps" size="small"></ElRadioButton>
                  <ElRadioButton value="baidu" label="百度" size="small"></ElRadioButton>
                </ElRadioGroup>
              </div>
            </div>
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                点标记数据url
                <Tip content={'可引用全局状态变量state的属性，例子：/abc/:def引用state.def'}></Tip>
              </ElText>
              <div class={classes['params-item-content']}>
                <ElInput
                  modelValue={this.props.markersDataUrl}
                  onUpdate:modelValue={(v) => (this.props.markersDataUrl = v || '')}
                  size="small"
                ></ElInput>
              </div>
            </div>
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                接口数据处理函数
                <Tip
                  content={'接口url不为空时启用；可访问全局状态变量state，res：接口返回数据'}
                ></Tip>
              </ElText>
              <div class={classes['params-item-content']}>
                <CodeEditor
                  modelValue={this.props.markersDataFn}
                  onUpdate:modelValue={(v) => (this.props.markersDataFn = v || '')}
                ></CodeEditor>
              </div>
            </div>
          </pParams>
        )
      })
    )
  }

  toVue(): SfcObj {
    const sfcObj = super.toVue()
    const name = sfcObj.name
    const [uuid0, uuid1] = this.uuid.split('-')
    const optionVar = `${name}_${uuid0}_option`
    const markersDataVar = `${name}_${uuid0}_markersData`
    const initMarkersDataVar = `${name}_${uuid0}_initMarkersData`
    const initDataVar = `${name}_${uuid0}_initData`

    const optionStr = JSON.stringify(this.props.option)

    sfcObj.imports = {
      ...sfcObj.imports,
      vue: {
        ref: 'ref',
        watch: 'watch'
      },
      ['./imports/Map.vue']: {
        default: 'Map'
      },
      ['./imports/request.js']: {
        default: 'request'
      },
      ['./imports/utils.js']: {
        gps2Amap: 'gps2Amap',
        baidu2Amap: 'baidu2Amap'
      }
    }

    sfcObj.importFiles = {
      ...sfcObj.importFiles,
      'Map.vue': MapRaw,
      'request.js': requestRaw,
      'utils.js': utilsRaw
    }

    sfcObj.script = `
${sfcObj.script}
const ${optionVar} = ref(${optionStr})
const ${markersDataVar} = ref([])
const ${initMarkersDataVar} = async () => {
  await request({
    url: '${this.props.markersDataUrl}'.replace(/\\:(\\w+)/g, (match, p1) => state[p1]),
    method: 'GET'
  }).then((res) => {
    const resData = (${this.props.markersDataFn || '() => []'})(res)
    resData.forEach((item) => {
      ${this.props.coordType == 'gps' ? 'item.position = gps2Amap(item.position)' : ''}
      ${this.props.coordType == 'baidu' ? 'item.position = baidu2Amap(item.position)' : ''}
    })
    ${markersDataVar}.value = resData
  })
}

const ${initDataVar} = async () => {
  ${initMarkersDataVar}()
}

${initDataVar}()
${
  this.props.watchState.length > 0
    ? `
${this.props.watchState
  .map((obj) => {
    if (obj.type == 'initMarkersDataVar')
      return `watch(() => state.${obj.stateProperty}, ${initMarkersDataVar})`
  })
  .join('\n')}
    `
    : ''
}
    `

    sfcObj.template = sfcObj.template.replace(
      '<slot></slot>',
      `
  <Map :option="${optionVar}" :markersData="${markersDataVar}"></Map>
      `
    )

    return sfcObj
  }
}
